"""
Classification command for CodeMCP CLI.

Classifies functions as local vs library, entry points vs utilities,
and provides detailed function categorization across projects.
"""

import argparse
import asyncio
from typing import List, Dict, Any, Optional
from pathlib import Path
import json

from ...core.function_classifier import FunctionClassifier, ClassificationConfig, FunctionClassification
from ...core.project_scanner import ProjectScanner, ProjectScanConfig
from ...core.entry_discovery import EntryPointDiscovery, EntryDiscoveryConfig
from ...core.error_handler import CodeMCPError, log_info, log_debug
from ..base_command import CLICommand


class ClassifyCommand(CLICommand):
    """Command for classifying functions and code components."""
    
    def add_parser(self, subparsers) -> argparse.ArgumentParser:
        """Add classify command parser."""
        parser = subparsers.add_parser(
            'classify',
            help='Classify functions as local/library, entry points, utilities, etc.',
            description='Analyze and classify functions by type, scope, and purpose',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
Examples:
  codemcp classify                          # Classify all functions in current directory
  codemcp classify --path /path/to/project  # Classify functions in specific project
  codemcp classify --file main.py          # Classify functions in specific file
  codemcp classify --show-local-only       # Show only local (non-library) functions
  codemcp classify --show-entry-points     # Show only entry point functions
  codemcp classify --language python       # Only classify Python functions
  codemcp classify --export-deps deps.json # Export dependency analysis
            """.strip()
        )
        
        # Target options
        parser.add_argument(
            '--path', '-p',
            type=str,
            default='.',
            help='Project path to analyze (default: current directory)'
        )
        parser.add_argument(
            '--file', '-f',
            type=str,
            help='Specific file to analyze'
        )
        parser.add_argument(
            '--function',
            type=str,
            help='Specific function to classify'
        )
        
        # Language filtering
        parser.add_argument(
            '--language', '-l',
            choices=['python', 'cpp', 'typescript', 'fortran', 'all'],
            default='all',
            help='Language to focus on (default: all)'
        )
        
        # Classification options
        parser.add_argument(
            '--classification-depth',
            choices=['basic', 'detailed', 'comprehensive'],
            default='detailed',
            help='Depth of classification analysis (default: detailed)'
        )
        parser.add_argument(
            '--include-library-functions',
            action='store_true',
            help='Include library/external functions in classification'
        )
        parser.add_argument(
            '--analyze-dependencies',
            action='store_true',
            help='Perform detailed dependency analysis'
        )
        
        # Filtering options
        parser.add_argument(
            '--show-local-only',
            action='store_true',
            help='Show only local (project-defined) functions'
        )
        parser.add_argument(
            '--show-library-only',
            action='store_true',
            help='Show only library/external functions'
        )
        parser.add_argument(
            '--show-entry-points',
            action='store_true',
            help='Show only entry point functions'
        )
        parser.add_argument(
            '--show-utilities',
            action='store_true',
            help='Show only utility functions'
        )
        parser.add_argument(
            '--show-tests',
            action='store_true',
            help='Show only test functions'
        )
        parser.add_argument(
            '--min-confidence',
            type=float,
            default=0.6,
            help='Minimum classification confidence (0.0-1.0, default: 0.6)'
        )
        
        # Output options
        parser.add_argument(
            '--show-details',
            action='store_true',
            help='Show detailed classification information'
        )
        parser.add_argument(
            '--show-call-relationships',
            action='store_true',
            help='Show function call relationships'
        )
        parser.add_argument(
            '--group-by',
            choices=['type', 'scope', 'file', 'language'],
            default='type',
            help='Group results by type, scope, file, or language (default: type)'
        )
        parser.add_argument(
            '--sort-by',
            choices=['name', 'confidence', 'complexity', 'calls'],
            default='name',
            help='Sort results by name, confidence, complexity, or calls (default: name)'
        )
        parser.add_argument(
            '--export-results',
            type=str,
            help='Export classification results to JSON file'
        )
        parser.add_argument(
            '--export-dependencies',
            type=str,
            help='Export dependency analysis to JSON file'
        )
        
        # Analysis options
        parser.add_argument(
            '--analyze-complexity',
            action='store_true',
            help='Include complexity analysis in classification'
        )
        parser.add_argument(
            '--find-orphaned',
            action='store_true',
            help='Find orphaned functions (defined but never called)'
        )
        parser.add_argument(
            '--find-entry-candidates',
            action='store_true',
            help='Find potential entry point candidates'
        )
        
        return parser
    
    async def execute(self, args: argparse.Namespace) -> int:
        """Execute the classify command."""
        try:
            self.printer.print_info("🔍 Starting function classification...")
            
            # Validate inputs
            project_path = Path(args.path).resolve()
            if not project_path.exists():
                raise CodeMCPError(f"Project path does not exist: {project_path}")
            
            if args.file:
                file_path = Path(args.file).resolve()
                if not file_path.exists():
                    raise CodeMCPError(f"File does not exist: {file_path}")
            
            # Setup configuration
            classification_config = self._create_classification_config(args)
            scan_config = self._create_scan_config(args)
            
            # Initialize components
            classifier = FunctionClassifier(classification_config)
            project_scanner = ProjectScanner(scan_config)
            
            # Perform classification
            results = await self._perform_classification(
                classifier, project_scanner, project_path, args
            )
            
            # Process and display results
            await self._display_results(results, args)
            
            # Export results if requested
            if args.export_results:
                await self._export_results(results, args.export_results)
            
            if args.export_dependencies:
                await self._export_dependencies(results, args.export_dependencies)
            
            self.printer.print_success("✅ Classification completed successfully!")
            return 0
            
        except Exception as e:
            self.printer.print_error(f"Classification failed: {e}")
            log_debug(f"Classification error details: {e}")
            return 1
    
    def _create_classification_config(self, args: argparse.Namespace) -> ClassificationConfig:
        """Create function classification configuration."""
        depth_map = {
            'basic': 1,
            'detailed': 2,
            'comprehensive': 3
        }
        
        return ClassificationConfig(
            analysis_depth=depth_map[args.classification_depth],
            include_library_functions=args.include_library_functions,
            analyze_dependencies=args.analyze_dependencies,
            analyze_complexity=args.analyze_complexity,
            min_confidence=args.min_confidence,
            find_orphaned_functions=args.find_orphaned,
            detect_entry_candidates=args.find_entry_candidates
        )
    
    def _create_scan_config(self, args: argparse.Namespace) -> ProjectScanConfig:
        """Create project scan configuration."""
        return ProjectScanConfig(
            deep_scan=True,
            include_dependencies=args.analyze_dependencies,
            ignore_build_dirs=True,
            ignore_cache_dirs=True,
            max_depth=15
        )
    
    async def _perform_classification(
        self,
        classifier: FunctionClassifier,
        project_scanner: ProjectScanner,
        project_path: Path,
        args: argparse.Namespace
    ) -> Dict[str, Any]:
        """Perform the actual classification process."""
        results = {
            'project_path': str(project_path),
            'classifications': [],
            'project_info': {},
            'statistics': {},
            'dependencies': {},
            'analysis_metadata': {
                'classification_depth': args.classification_depth,
                'languages_analyzed': [],
                'files_processed': 0
            }
        }
        
        with self.progress.spinner("Scanning project structure..."):
            # Scan project structure
            project_info = project_scanner.scan_project(project_path)
            results['project_info'] = project_info
        
        with self.progress.spinner("Classifying functions..."):
            if args.file:
                # Classify specific file
                file_path = Path(args.file).resolve()
                if args.function:
                    # Classify specific function
                    classification = classifier.classify_function(
                        str(file_path), args.function, project_path
                    )
                    results['classifications'] = [classification.__dict__]
                else:
                    # Classify all functions in file
                    classifications = classifier.classify_file_functions(
                        str(file_path), project_path
                    )
                    results['classifications'] = [c.__dict__ for c in classifications]
            else:
                # Classify entire project
                classifications = classifier.classify_project_functions(
                    project_path,
                    language_filter=None if args.language == 'all' else args.language
                )
                results['classifications'] = [c.__dict__ for c in classifications]
        
        # Analyze dependencies if requested
        if args.analyze_dependencies:
            with self.progress.spinner("Analyzing dependencies..."):
                dependency_analysis = classifier.analyze_project_dependencies(project_path)
                results['dependencies'] = dependency_analysis
        
        # Generate statistics
        results['statistics'] = self._generate_classification_statistics(
            results['classifications'], project_info
        )
        
        # Update metadata
        results['analysis_metadata']['files_processed'] = len(
            results['classifications']
        )
        results['analysis_metadata']['languages_analyzed'] = list(
            set(c.get('language', 'unknown') for c in results['classifications'])
        )
        
        return results
    
    def _generate_classification_statistics(
        self,
        classifications: List[Dict[str, Any]],
        project_info: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Generate classification statistics."""
        stats = {
            'total_functions': len(classifications),
            'by_scope': {'local': 0, 'library': 0, 'unknown': 0},
            'by_type': {},
            'by_language': {},
            'by_confidence': {'high': 0, 'medium': 0, 'low': 0},
            'complexity_distribution': {'low': 0, 'medium': 0, 'high': 0},
            'entry_points': 0,
            'orphaned_functions': 0,
            'test_functions': 0
        }
        
        for classification in classifications:
            # Count by scope
            scope = classification.get('scope', 'unknown')
            stats['by_scope'][scope] = stats['by_scope'].get(scope, 0) + 1
            
            # Count by type
            func_type = classification.get('function_type', 'unknown')
            stats['by_type'][func_type] = stats['by_type'].get(func_type, 0) + 1
            
            # Count by language
            language = classification.get('language', 'unknown')
            stats['by_language'][language] = stats['by_language'].get(language, 0) + 1
            
            # Count by confidence
            confidence = classification.get('confidence', 0)
            if confidence >= 0.8:
                stats['by_confidence']['high'] += 1
            elif confidence >= 0.6:
                stats['by_confidence']['medium'] += 1
            else:
                stats['by_confidence']['low'] += 1
            
            # Count special types
            if func_type == 'entry_point':
                stats['entry_points'] += 1
            elif classification.get('is_orphaned', False):
                stats['orphaned_functions'] += 1
            elif func_type == 'test':
                stats['test_functions'] += 1
            
            # Complexity distribution
            complexity = classification.get('complexity', {}).get('level', 'unknown')
            if complexity in stats['complexity_distribution']:
                stats['complexity_distribution'][complexity] += 1
        
        return stats
    
    async def _display_results(self, results: Dict[str, Any], args: argparse.Namespace):
        """Display classification results."""
        classifications = results['classifications']
        statistics = results['statistics']
        project_info = results['project_info']
        
        # Display project information
        self.printer.print_header("📁 Project Information")
        self.formatter.print_key_value_pairs({
            "Project Path": results['project_path'],
            "Languages": ', '.join(results['analysis_metadata']['languages_analyzed']),
            "Files Processed": results['analysis_metadata']['files_processed'],
            "Classification Depth": results['analysis_metadata']['classification_depth'].title()
        })
        
        # Display statistics
        self.printer.print_header("📊 Classification Statistics")
        
        self.formatter.print_key_value_pairs({
            "Total Functions": statistics['total_functions'],
            "Local Functions": statistics['by_scope']['local'],
            "Library Functions": statistics['by_scope']['library'],
            "Entry Points": statistics['entry_points'],
            "Test Functions": statistics['test_functions']
        })
        
        if args.find_orphaned and statistics['orphaned_functions'] > 0:
            self.printer.print_warning(f"⚠️  Found {statistics['orphaned_functions']} orphaned functions")
        
        # Display confidence distribution
        self.printer.print_subheader("Confidence Distribution:")
        confidence_stats = statistics['by_confidence']
        total = sum(confidence_stats.values())
        if total > 0:
            for level, count in confidence_stats.items():
                percentage = (count / total) * 100
                self.printer.print_info(f"  {level.title()}: {count} ({percentage:.1f}%)")
        
        # Display function type distribution
        if statistics['by_type']:
            self.printer.print_subheader("Function Types:")
            for func_type, count in statistics['by_type'].items():
                self.printer.print_info(f"  {func_type}: {count}")
        
        # Filter classifications based on arguments
        filtered_classifications = self._filter_classifications(classifications, args)
        
        if filtered_classifications:
            self.printer.print_header("🏷️  Function Classifications")
            
            # Sort classifications
            sorted_classifications = self._sort_classifications(filtered_classifications, args.sort_by)
            
            # Group and display
            if args.group_by == 'type':
                self._display_grouped_by_type(sorted_classifications, args)
            elif args.group_by == 'scope':
                self._display_grouped_by_scope(sorted_classifications, args)
            elif args.group_by == 'file':
                self._display_grouped_by_file(sorted_classifications, args)
            elif args.group_by == 'language':
                self._display_grouped_by_language(sorted_classifications, args)
            else:
                self._display_classifications_list(sorted_classifications, args)
        else:
            self.printer.print_warning("No functions match the specified filters.")
    
    def _filter_classifications(
        self, 
        classifications: List[Dict[str, Any]], 
        args: argparse.Namespace
    ) -> List[Dict[str, Any]]:
        """Filter classifications based on command arguments."""
        filtered = classifications
        
        # Apply scope filters
        if args.show_local_only:
            filtered = [c for c in filtered if c.get('scope') == 'local']
        elif args.show_library_only:
            filtered = [c for c in filtered if c.get('scope') == 'library']
        
        # Apply type filters
        if args.show_entry_points:
            filtered = [c for c in filtered if c.get('function_type') == 'entry_point']
        elif args.show_utilities:
            filtered = [c for c in filtered if c.get('function_type') == 'utility']
        elif args.show_tests:
            filtered = [c for c in filtered if c.get('function_type') == 'test']
        
        # Apply confidence filter
        filtered = [c for c in filtered if c.get('confidence', 0) >= args.min_confidence]
        
        # Apply language filter
        if args.language != 'all':
            filtered = [c for c in filtered if c.get('language') == args.language]
        
        return filtered
    
    def _sort_classifications(
        self, 
        classifications: List[Dict[str, Any]], 
        sort_by: str
    ) -> List[Dict[str, Any]]:
        """Sort classifications by specified criteria."""
        if sort_by == 'name':
            return sorted(classifications, key=lambda c: c.get('function_name', ''))
        elif sort_by == 'confidence':
            return sorted(classifications, key=lambda c: c.get('confidence', 0), reverse=True)
        elif sort_by == 'complexity':
            return sorted(classifications, key=lambda c: c.get('complexity', {}).get('score', 0), reverse=True)
        elif sort_by == 'calls':
            return sorted(classifications, key=lambda c: len(c.get('calls_made', [])), reverse=True)
        else:
            return classifications
    
    def _display_grouped_by_type(self, classifications: List[Dict], args: argparse.Namespace):
        """Display classifications grouped by function type."""
        type_map = {}
        for classification in classifications:
            func_type = classification.get('function_type', 'unknown')
            if func_type not in type_map:
                type_map[func_type] = []
            type_map[func_type].append(classification)
        
        for func_type, class_list in type_map.items():
            self.printer.print_subheader(f"🏷️  {func_type.replace('_', ' ').title()}")
            for classification in class_list:
                self._display_classification(classification, args, indent=2)
    
    def _display_grouped_by_scope(self, classifications: List[Dict], args: argparse.Namespace):
        """Display classifications grouped by scope."""
        scope_map = {}
        for classification in classifications:
            scope = classification.get('scope', 'unknown')
            if scope not in scope_map:
                scope_map[scope] = []
            scope_map[scope].append(classification)
        
        for scope, class_list in scope_map.items():
            icon = "🏠" if scope == 'local' else "📚" if scope == 'library' else "❓"
            self.printer.print_subheader(f"{icon} {scope.title()}")
            for classification in class_list:
                self._display_classification(classification, args, indent=2)
    
    def _display_grouped_by_file(self, classifications: List[Dict], args: argparse.Namespace):
        """Display classifications grouped by file."""
        file_map = {}
        for classification in classifications:
            file_path = classification.get('file_path', 'Unknown')
            if file_path not in file_map:
                file_map[file_path] = []
            file_map[file_path].append(classification)
        
        for file_path, class_list in file_map.items():
            self.printer.print_subheader(f"📄 {file_path}")
            for classification in class_list:
                self._display_classification(classification, args, indent=2)
    
    def _display_grouped_by_language(self, classifications: List[Dict], args: argparse.Namespace):
        """Display classifications grouped by language."""
        lang_map = {}
        for classification in classifications:
            language = classification.get('language', 'unknown')
            if language not in lang_map:
                lang_map[language] = []
            lang_map[language].append(classification)
        
        for language, class_list in lang_map.items():
            self.printer.print_subheader(f"🔤 {language.title()}")
            for classification in class_list:
                self._display_classification(classification, args, indent=2)
    
    def _display_classifications_list(self, classifications: List[Dict], args: argparse.Namespace):
        """Display classifications as a simple list."""
        for i, classification in enumerate(classifications, 1):
            self.printer.print_info(f"{i}. ", end="")
            self._display_classification(classification, args)
    
    def _display_classification(self, classification: Dict, args: argparse.Namespace, indent: int = 0):
        """Display a single function classification."""
        indent_str = "  " * indent
        
        # Basic info
        name = classification.get('function_name', 'Unknown')
        func_type = classification.get('function_type', 'unknown')
        scope = classification.get('scope', 'unknown')
        confidence = classification.get('confidence', 0)
        
        # Scope indicator
        scope_icon = "🏠" if scope == 'local' else "📚" if scope == 'library' else "❓"
        
        # Confidence indicator
        if confidence >= 0.8:
            confidence_indicator = "🟢"
        elif confidence >= 0.6:
            confidence_indicator = "🟡"
        else:
            confidence_indicator = "🔴"
        
        # Format basic line
        basic_info = f"{indent_str}{confidence_indicator} {scope_icon} {name} ({func_type})"
        self.printer.print_info(basic_info)
        
        if args.show_details:
            # Show detailed information
            details = {
                "File": classification.get('file_path', 'Unknown'),
                "Line": classification.get('line_number', 'Unknown'),
                "Scope": scope,
                "Confidence": f"{confidence:.2f}",
                "Language": classification.get('language', 'Unknown')
            }
            
            # Add complexity if available
            complexity = classification.get('complexity', {})
            if complexity:
                details["Complexity"] = f"{complexity.get('level', 'unknown')} ({complexity.get('score', 0)})"
            
            for key, value in details.items():
                self.printer.print_info(f"{indent_str}  {key}: {value}")
            
            # Show call relationships if requested
            if args.show_call_relationships:
                calls_made = classification.get('calls_made', [])
                called_by = classification.get('called_by', [])
                
                if calls_made:
                    self.printer.print_info(f"{indent_str}  Calls: {', '.join(calls_made)}")
                if called_by:
                    self.printer.print_info(f"{indent_str}  Called by: {', '.join(called_by)}")
    
    async def _export_results(self, results: Dict[str, Any], output_file: str):
        """Export classification results to file."""
        try:
            output_path = Path(output_file)
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(results, f, indent=2, ensure_ascii=False)
            
            self.printer.print_success(f"💾 Classification results saved to {output_path}")
            
        except Exception as e:
            self.printer.print_error(f"Failed to export results: {e}")
    
    async def _export_dependencies(self, results: Dict[str, Any], output_file: str):
        """Export dependency analysis to file."""
        try:
            output_path = Path(output_file)
            dependencies = results.get('dependencies', {})
            
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(dependencies, f, indent=2, ensure_ascii=False)
            
            self.printer.print_success(f"💾 Dependencies exported to {output_path}")
            
        except Exception as e:
            self.printer.print_error(f"Failed to export dependencies: {e}")